﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Nvigorate.Common;
using Nvigorate.Relational.Mapping;
using Nvigorate.Relational.Mapping.Fluent;
using Nvigorate.Relational.Mapping.Index;
using Nvigorate.Test.Relational.Binding.MultipleClassTest;
using Nvigorate.Test.Relational.Binding.SingleClassTest;

namespace Nvigorate.Test.Relational.Binding
{
    [TestClass]
    public class ScanTargetMapLoaderTests : BindingTestBase
    {
        #region GetMaps Tests

        [TestMethod]
        public void GetMaps_NoXmlNamespaceFilePathHasBindingFile_GetsMapFromFile()
        {
            // Arrange
            var fileName = "Employee.binding";
            var target = new ScanTarget {MapDirectory = "c:\\test"};
            var bindingMapLoader = CreateBindingMapLoader("Employee");

            var fileHelper = new Mock<IFileHelper>();
            fileHelper.Expect(f => f.GetFileNamesForPath("c:\\test", "*.binding")).Returns(
                new List<string> {fileName});

            var bindingMapFactory = new Mock<IBindingMapLoaderFactory>();
            bindingMapFactory.Expect(f => f.GetBindingMapLoader(fileName, It.IsAny<Type>())).Returns(
                bindingMapLoader);

            // Act
            var loader = new ScanTargetMapLoader(new[] {target}, typeof (BindingMap), bindingMapFactory.Object,
                                                 fileHelper.Object);
            var actual = loader.GetMaps().ToList();

            // Assert
            Assert.AreEqual(1, actual.Count);
            Assert.AreEqual("Employee", actual[0].RepositoryName);
        }

        [TestMethod]
        public void GetMaps_NoXmlNamespaceFilePathHasMultipleBindingFiles_GetsMapsFromFiles()
        {
            // Arrange
            var fileNames = new[] {"Employee.binding", "Customer.binding"};
            var target = new ScanTarget {MapDirectory = "c:\\test"};
            var employeeBindingMapLoader = CreateBindingMapLoader("Employee");
            var customerBindingMapLoader = CreateBindingMapLoader("Customer");

            var fileHelper = new Mock<IFileHelper>();
            fileHelper.Expect(f => f.GetFileNamesForPath("c:\\test", "*.binding")).Returns(
                new List<string>(fileNames));

            var bindingMapFactory = new Mock<IBindingMapLoaderFactory>();
            bindingMapFactory.Expect(f => f.GetBindingMapLoader(fileNames[0], It.IsAny<Type>())).Returns(employeeBindingMapLoader);
            bindingMapFactory.Expect(f => f.GetBindingMapLoader(fileNames[1], It.IsAny<Type>())).Returns(customerBindingMapLoader);

            // Act
            var loader = new ScanTargetMapLoader(new[] {target}, typeof (BindingMap), bindingMapFactory.Object,
                                                 fileHelper.Object);
            var actual = loader.GetMaps().ToList();

            // Assert
            Assert.AreEqual(2, actual.Count);
            Assert.IsNotNull(actual.FirstOrDefault(m => m.RepositoryName == "Employee"));
            Assert.IsNotNull(actual.FirstOrDefault(m => m.RepositoryName == "Customer"));
        }

        [TestMethod]
        public void GetMaps_WithXmlNamespaceAndOneDefinedType_GetsMapsFromTypeDefinedInNamespace()
        {
            // Arrange
            var target = new ScanTarget {Namespace = typeof(DeclarativeMapTypeA).Namespace};
            var bindingMapLoader = CreateBindingMapLoader("TestSource");
            var bindingMapFactory = new Mock<IBindingMapLoaderFactory>();
            bindingMapFactory.Expect(f => f.GetBindingMapLoader(typeof (DeclarativeMapTypeAMap))).Returns(
                bindingMapLoader);

            // Act
            var loader = new ScanTargetMapLoader(new[] {target}, typeof (BindingMap), bindingMapFactory.Object);
            var actual = loader.GetMaps().ToList();

            // Assert
            Assert.AreEqual(1, actual.Count);
            Assert.AreEqual("TestSource", actual[0].RepositoryName);
        }

        [TestMethod]
        public void GetMaps_WithXmlNamespaceAndMultipleDefinedTypes_GetsMapsFromTypesDefinedInNamespace()
        {
            // Arrange
            var target = new ScanTarget { Namespace = typeof(DeclarativeMapTypeB).Namespace };
            var bindingMapLoaderB = CreateBindingMapLoader("TypeB");
            var bindingMapLoaderC = CreateBindingMapLoader("TypeC");

            var bindingMapFactory = new Mock<IBindingMapLoaderFactory>();
            bindingMapFactory.Expect(f => f.GetBindingMapLoader(typeof(DeclarativeMapTypeBMap))).Returns(
                bindingMapLoaderB);
            bindingMapFactory.Expect(f => f.GetBindingMapLoader(typeof (DeclarativeMapTypeCMap))).
                Returns(
                bindingMapLoaderC);

            // Act
            var loader = new ScanTargetMapLoader(new[] { target }, typeof(BindingMap), bindingMapFactory.Object);
            var actual = loader.GetMaps().ToList();

            // Assert
            Assert.AreEqual(2, actual.Count);
            Assert.IsNotNull(actual.FirstOrDefault(m => m.RepositoryName == "TypeB"));
            Assert.IsNotNull(actual.FirstOrDefault(m => m.RepositoryName == "TypeC"));
            
        }
        #endregion

    }

    #region Test Harness Types

    namespace SingleClassTest
    {
        public class DeclarativeMapTypeA
        {
            public string StringField { get; set; }
            public int IntField { get; set; }
        }

        public class DeclarativeMapTypeAMap : FluentMap<DeclarativeMapTypeA>
        {
            public DeclarativeMapTypeAMap()
            {
                RepositoryName = "MapSetA";
                MapTable("Table1");
            }
        }
    
    }

    namespace MultipleClassTest
    {
        public class DeclarativeMapTypeB
        {
            public string StringField { get; set; }
            public int IntField { get; set; }
        }

        public class DeclarativeMapTypeBMap : FluentMap<DeclarativeMapTypeB>
        {
            public DeclarativeMapTypeBMap()
            {
                RepositoryName = "MapSetB";
                MapTable("Table2");
            }
        }
    
        public class DeclarativeMapTypeC
        {
            public DateTime DateField { get; set; }
        }

        public class DeclarativeMapTypeCMap : FluentMap<DeclarativeMapTypeC>
        {
            public DeclarativeMapTypeCMap()
            {
                RepositoryName = "MapSetB";
                MapTable("Table3");
            }
        }
    }
    #endregion
}